Passed
Push — master ( 92cd86...dacf94 )
by Zhenyu
01:30
created

enhancer-creator.js ➔ ???   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 87

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
c 1
b 0
f 1
nc 1
dl 0
loc 87
rs 8.6296
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
import compose from 'compose-function';
2
3
import createEnhancer from '../enhancer-creator';
4
5
describe('createEnhancer can create enhancer', () => {
6
	describe('when input function', () => {
7
		it('output enhanced function with original name', () => {
8
			const orignal = () => {};
9
			const enhancement = input => () => input();
10
			const enhancer = createEnhancer(enhancement);
11
			const enhanced = enhancer(orignal);
12
			expect(enhanced.name).toBe('orignal');
13
		});
14
15
		it('output enhanced function invoking enhancement function and orignal function', () => {
16
			const enhancementSideEffect = jest.fn();
17
			const orignal = jest.fn();
18
			const enhancer = createEnhancer(input => () => {
19
				enhancementSideEffect();
20
				return input();
21
			});
22
			const enhanced = enhancer(orignal);
23
			enhanced();
24
			expect(orignal.mock.calls).toHaveLength(1);
25
			expect(enhancementSideEffect.mock.calls).toHaveLength(1);
26
		});
27
	});
28
29
	describe('when input function bundle', () => {
30
		it('output function bundle of functions name as method names', () => {
31
			const functionBundle = {
32
				methodA: () => {},
33
				methodB: () => {},
34
			};
35
			const enhancer = createEnhancer(input => () => input());
36
			const enhanced = enhancer(functionBundle);
37
			expect(enhanced.methodA.name).toBe('methodA');
38
			expect(enhanced.methodB.name).toBe('methodB');
39
		});
40
41
		it('update function names in the original bundle methods, so that they can be consistent in enhancement function', () => {
42
			const enhancementSideEffect = jest.fn();
43
			const functionBundle = {
44
				methodA: jest.fn(),
45
				methodB: jest.fn(),
46
			};
47
			const enhancer = createEnhancer(inputFunction => () => {
48
				enhancementSideEffect(inputFunction.name);
49
				return inputFunction();
50
			});
51
			const enhanced = enhancer(functionBundle);
52
			enhanced.methodA();
53
			expect(enhancementSideEffect.mock.calls).toMatchSnapshot();
54
			enhanced.methodB();
55
			expect(enhancementSideEffect.mock.calls).toMatchSnapshot();
56
		});
57
58
		it('output function bundle of functions invoking enhancement function and origial functions', () => {
59
			const enhancementSideEffect = jest.fn();
60
			const methodA = jest.fn();
61
			const methodB = jest.fn();
62
			const functionBundle = {
63
				methodA,
64
				methodB,
65
			};
66
			const enhancer = createEnhancer(input => () => {
67
				enhancementSideEffect();
68
				return input();
69
			});
70
			const enhanced = enhancer(functionBundle);
71
			enhanced.methodA();
72
			expect(methodA.mock.calls).toHaveLength(1);
73
			expect(enhancementSideEffect.mock.calls).toHaveLength(1);
74
			enhanced.methodB();
75
			expect(methodB.mock.calls).toHaveLength(1);
76
			expect(enhancementSideEffect.mock.calls).toHaveLength(2);
77
		});
78
	});
79
80
	describe('that is chainable with enhancers of the same target function signature', () => {
81
		describe('invokes enhancement function in correct order so that data can be passed in', () => {
82
			it('non-async enhancement functions', () => {
83
				const callOrderFunction = jest.fn();
84
				const dataStreamFunction = jest.fn();
85
				const targetFunction = meta => {
86
					callOrderFunction('targetFunction');
87
					dataStreamFunction(meta);
88
					return meta;
89
				};
90
				const enhancerA = createEnhancer(inputFunction => meta => {
91
					callOrderFunction('enhancerA');
92
					dataStreamFunction(meta);
93
					const updatedMeta = { ...meta, enhancerA: 'data added' };
94
					return inputFunction(updatedMeta);
95
				});
96
				const enhancerB = createEnhancer(inputFunction => meta => {
97
					callOrderFunction('enhancerB');
98
					dataStreamFunction(meta);
99
					const updatedMeta = { ...meta, enhancerB: 'data added' };
100
					return inputFunction(updatedMeta);
101
				});
102
				const initialMeta = { initial: 'data added' };
103
				const enhanced = compose(enhancerA, enhancerB)(targetFunction);
104
				const result = enhanced(initialMeta);
105
				expect(callOrderFunction.mock.calls).toMatchSnapshot();
106
				expect(dataStreamFunction.mock.calls).toMatchSnapshot();
107
				expect(result).toMatchSnapshot();
108
109
				const reverseEnhanced = compose(enhancerB, enhancerA)(targetFunction);
110
				const reverseResult = reverseEnhanced(initialMeta);
111
				expect(callOrderFunction.mock.calls).toMatchSnapshot();
112
				expect(dataStreamFunction.mock.calls).toMatchSnapshot();
113
				expect(reverseResult).toMatchSnapshot();
114
			});
115
116
			it('async enhancement functions', async () => {
117
				const callOrderFunction = jest.fn();
118
				const dataStreamFunction = jest.fn();
119
				const targetFunction = meta => {
120
					callOrderFunction('targetFunction');
121
					dataStreamFunction(meta);
122
					return meta;
123
				};
124
				const enhancerA = createEnhancer(inputFunction => async meta => {
125
					callOrderFunction('enhancerA');
126
					await dataStreamFunction(meta);
127
					const updatedMeta = { ...meta, enhancerA: 'data added' };
128
					return inputFunction(updatedMeta);
129
				});
130
				const enhancerB = createEnhancer(inputFunction => async meta => {
131
					callOrderFunction('enhancerB');
132
					await dataStreamFunction(meta);
133
					const updatedMeta = { ...meta, enhancerB: 'data added' };
134
					return inputFunction(updatedMeta);
135
				});
136
				const initialMeta = { initial: 'data added' };
137
				const enhanced = compose(enhancerA, enhancerB)(targetFunction);
138
				const result = await enhanced(initialMeta);
139
				expect(callOrderFunction.mock.calls).toMatchSnapshot();
140
				expect(dataStreamFunction.mock.calls).toMatchSnapshot();
141
				expect(result).toMatchSnapshot();
142
143
				const reverseEnhanced = compose(enhancerB, enhancerA)(targetFunction);
144
				const reverseResult = await reverseEnhanced(initialMeta);
145
				expect(callOrderFunction.mock.calls).toMatchSnapshot();
146
				expect(dataStreamFunction.mock.calls).toMatchSnapshot();
147
				expect(reverseResult).toMatchSnapshot();
148
			});
149
150
			it('mixed non-async and async enhancement functions', async () => {
151
				const callOrderFunction = jest.fn();
152
				const dataStreamFunction = jest.fn();
153
				const targetFunction = meta => {
154
					callOrderFunction('targetFunction');
155
					dataStreamFunction(meta);
156
					return meta;
157
				};
158
				const enhancerA = createEnhancer(inputFunction => async meta => {
159
					callOrderFunction('enhancerA');
160
					await dataStreamFunction(meta);
161
					const updatedMeta = { ...meta, enhancerA: 'data added' };
162
					return inputFunction(updatedMeta);
163
				});
164
				const enhancerB = createEnhancer(inputFunction => meta => {
165
					callOrderFunction('enhancerB');
166
					dataStreamFunction(meta);
167
					const updatedMeta = { ...meta, enhancerB: 'data added' };
168
					return inputFunction(updatedMeta);
169
				});
170
				const initialMeta = { initial: 'data added' };
171
				const enhanced = compose(enhancerA, enhancerB)(targetFunction);
172
				const result = await enhanced(initialMeta);
173
				expect(callOrderFunction.mock.calls).toMatchSnapshot();
174
				expect(dataStreamFunction.mock.calls).toMatchSnapshot();
175
				expect(result).toMatchSnapshot();
176
177
				const reverseEnhanced = compose(enhancerB, enhancerA)(targetFunction);
178
				const reverseResult = await reverseEnhanced(initialMeta);
179
				expect(callOrderFunction.mock.calls).toMatchSnapshot();
180
				expect(dataStreamFunction.mock.calls).toMatchSnapshot();
181
				expect(reverseResult).toMatchSnapshot();
182
			});
183
184
			// TODO: further explore this implementation and its tests
185
			describe('promises based sideEffect functions so that target functions not blocked', () => {
186
				it('enhanced can be invoked without await if target function not async', () => {
187
					const callOrderFunction = jest.fn();
188
					const dataStreamFunction = jest.fn();
189
					const targetFunction = meta => {
190
						callOrderFunction('targetFunction');
191
						dataStreamFunction(meta);
192
						return meta;
193
					};
194
					const enhancerA = createEnhancer(inputFunction => meta => {
195
						callOrderFunction('enhancerA');
196
						Promise.resolve(() => dataStreamFunction(meta));
197
						const updatedMeta = { ...meta, enhancerA: 'data added' };
198
						return inputFunction(updatedMeta);
199
					});
200
					const enhancerB = createEnhancer(inputFunction => meta => {
201
						callOrderFunction('enhancerB');
202
						dataStreamFunction(meta);
203
						const updatedMeta = { ...meta, enhancerB: 'data added' };
204
						return inputFunction(updatedMeta);
205
					});
206
					const initialMeta = { initial: 'data added' };
207
					const enhanced = compose(enhancerA, enhancerB)(targetFunction);
208
					const result = enhanced(initialMeta);
209
					expect(callOrderFunction.mock.calls).toMatchSnapshot();
210
					expect(dataStreamFunction.mock.calls).toMatchSnapshot();
211
					expect(result).toMatchSnapshot();
212
213
					const reverseEnhanced = compose(enhancerB, enhancerA)(targetFunction);
214
					const reverseResult = reverseEnhanced(initialMeta);
215
					expect(callOrderFunction.mock.calls).toMatchSnapshot();
216
					expect(dataStreamFunction.mock.calls).toMatchSnapshot();
217
					expect(reverseResult).toMatchSnapshot();
218
				});
219
220
				it('enhanced can be invoked with await if target function async', () => {
221
					const callOrderFunction = jest.fn();
222
					const dataStreamFunction = jest.fn();
223
					const targetFunction = meta => {
224
						callOrderFunction('targetFunction');
225
						dataStreamFunction(meta);
226
						return meta;
227
					};
228
					const enhancerA = createEnhancer(inputFunction => meta => {
229
						callOrderFunction('enhancerA');
230
						Promise.resolve(() => dataStreamFunction(meta));
231
						const updatedMeta = { ...meta, enhancerA: 'data added' };
232
						return inputFunction(updatedMeta);
233
					});
234
					const enhancerB = createEnhancer(inputFunction => meta => {
235
						callOrderFunction('enhancerB');
236
						dataStreamFunction(meta);
237
						const updatedMeta = { ...meta, enhancerB: 'data added' };
238
						return inputFunction(updatedMeta);
239
					});
240
					const initialMeta = { initial: 'data added' };
241
					const enhanced = compose(enhancerA, enhancerB)(targetFunction);
242
					const result = enhanced(initialMeta);
243
					expect(callOrderFunction.mock.calls).toMatchSnapshot();
244
					expect(dataStreamFunction.mock.calls).toMatchSnapshot();
245
					expect(result).toMatchSnapshot();
246
247
					const reverseEnhanced = compose(enhancerB, enhancerA)(targetFunction);
248
					const reverseResult = reverseEnhanced(initialMeta);
249
					expect(callOrderFunction.mock.calls).toMatchSnapshot();
250
					expect(dataStreamFunction.mock.calls).toMatchSnapshot();
251
					expect(reverseResult).toMatchSnapshot();
252
				});
253
			});
254
255
			// enhancer doesn't need to be async
256
			// as long as await is used when invoke the enhanced
257
			it('async target function', async () => {
258
				const callOrderFunction = jest.fn();
259
				const dataStreamFunction = jest.fn();
260
				const targetFunction = async meta => {
261
					callOrderFunction('targetFunction');
262
					dataStreamFunction(meta);
263
					return meta;
264
				};
265
				const enhancerA = createEnhancer(inputFunction => async meta => {
266
					callOrderFunction('enhancerA');
267
					await dataStreamFunction(meta);
268
					const updatedMeta = { ...meta, enhancerA: 'data added' };
269
					return inputFunction(updatedMeta);
270
				});
271
				const enhancerB = createEnhancer(inputFunction => meta => {
272
					callOrderFunction('enhancerB');
273
					dataStreamFunction(meta);
274
					const updatedMeta = { ...meta, enhancerB: 'data added' };
275
					return inputFunction(updatedMeta);
276
				});
277
				const initialMeta = { initial: 'data added' };
278
				const enhanced = compose(enhancerA, enhancerB)(targetFunction);
279
				const result = await enhanced(initialMeta);
280
				expect(callOrderFunction.mock.calls).toMatchSnapshot();
281
				expect(dataStreamFunction.mock.calls).toMatchSnapshot();
282
				expect(result).toMatchSnapshot();
283
284
				const reverseEnhanced = compose(enhancerB, enhancerA)(targetFunction);
285
				const reverseResult = await reverseEnhanced(initialMeta);
286
				expect(callOrderFunction.mock.calls).toMatchSnapshot();
287
				expect(dataStreamFunction.mock.calls).toMatchSnapshot();
288
				expect(reverseResult).toMatchSnapshot();
289
			});
290
		});
291
	});
292
293
	it('when input is invalid throws error', () => {
294
		const enhancer = createEnhancer(input => () => input());
295
		const doEnhanceStringInput = () => enhancer('test');
296
		expect(doEnhanceStringInput).toThrowErrorMatchingSnapshot();
297
298
		const doEnhanceInvalidObject = () =>
299
			enhancer({
300
				foo: 'bar',
301
			});
302
		expect(doEnhanceInvalidObject).toThrowErrorMatchingSnapshot();
303
	});
304
});
305